/**
 * Copyright [2019] [LiBo/Alex of copyright liboware@gmail.com ]
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.hyts.standarcloud.security.gateway.config;

import com.alibaba.fastjson.JSONObject;
import com.hyts.standarcloud.security.gateway.factory.SecurityReactiveAuthorizationManager;
import com.hyts.standarcloud.security.gateway.model.SecurityAuthentication;
import com.hyts.standarcloud.security.gateway.convert.SecurityAuthenticationConverter;
import com.hyts.standarcloud.security.gateway.properties.SecurityGatewayProperties;
import io.netty.buffer.UnpooledByteBufAllocator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.NettyDataBufferFactory;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authentication.ReactiveAuthenticationManagerAdapter;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.authentication.AuthenticationWebFilter;
import org.springframework.security.web.server.authentication.RedirectServerAuthenticationEntryPoint;
import org.springframework.web.server.WebFilter;
import reactor.core.publisher.Flux;

import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.Objects;
import java.util.stream.Stream;

/**
 * @project-name:standard-cloud
 * @package-name:com.hyts.standarcloud.security.gateway
 * @author:LiBo/Alex
 * @create-date:2020-03-29 20:02
 * @copyright:libo-alex4java
 * @email:liboware@gmail.com
 * @description:
 */

@EnableConfigurationProperties
@EnableWebFluxSecurity
public class SecurityGatewayConfiguration {


    @Autowired
    SecurityGatewayProperties gatewayAccessProperties;

    /**
     * SpringSecurity处理的定义控制访问权限过滤器拦截链
     * @param http
     * @return
     */
    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
            // 重定向服务的登录页面服务控制器访问点
            RedirectServerAuthenticationEntryPoint loginPoint =
                new RedirectServerAuthenticationEntryPoint("/standard-cloud-facade/login");
                 http.authorizeExchange().pathMatchers(
                         "/standard-cloud-facade/css/**",
                         "/standard-cloud-facade/js/**",
                         "/standard-cloud-facade/images/**",
                         "/standard-cloud-facade/logo.png",
                         "/standard-cloud-facade/login",
                         "/standard-cloud-facade/account/login").permitAll()
                .and().formLogin().loginPage("/standard-cloud-facade/account/authen").authenticationEntryPoint(loginPoint)
                .authenticationSuccessHandler((webFilterExchange,authentication)->{
                    //认证成功之后返回给客户端的信息
                    JSONObject result = new JSONObject();
                    result.put("code", 0);
                    return webFilterExchange.getExchange().getResponse().writeWith(Flux.create(sink -> {
                        NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(
                                new UnpooledByteBufAllocator(false));
                        try {
                            DataBuffer dataBuffer= nettyDataBufferFactory.
                                    wrap(result.toJSONString().getBytes("utf8"));
                            sink.next(dataBuffer);
                        } catch (UnsupportedEncodingException e) {
                            e.printStackTrace();
                        }
                        sink.complete();
                    }));
                })
                .and().authorizeExchange()
                .pathMatchers("/standard-cloud-facade/index").
                 access(new SecurityReactiveAuthorizationManager("Manager", "Dev"))
                .and().authorizeExchange().anyExchange().authenticated()
                .and().csrf().disable();
                 SecurityWebFilterChain chain = http.build();
                 Iterator<WebFilter> weIterable = chain.getWebFilters().toIterable().iterator();
                 while(weIterable.hasNext()) {
                     WebFilter f = weIterable.next();
                    if(f instanceof AuthenticationWebFilter) {
                        AuthenticationWebFilter webFilter = (AuthenticationWebFilter) f;
                        //将自定义的AuthenticationConverter添加到过滤器中
                        webFilter.setServerAuthenticationConverter(new SecurityAuthenticationConverter());
                 }
            }
           return chain;
    }

    /**
     *
     * @return
     */
    @Bean
    public ReactiveAuthenticationManager reactiveAuthenticationManager() {
        return new ReactiveAuthenticationManagerAdapter((authentication)->{
            if(authentication instanceof SecurityAuthentication) {
                SecurityAuthentication gmAccountAuthentication = (SecurityAuthentication) authentication;
                if(Objects.nonNull(gmAccountAuthentication.getPrincipal())) {
                    authentication.setAuthenticated(true);
                    return authentication;
                } else {
                    return authentication;
                }
            } else {
                return authentication;
            }
        });
    }
}



